/* kvaser_canfdfuncs - Kvaser CAN FD hardware depending part of can4linux drivers
*
* can4linux -- LINUX CAN device driver source
* 
* This file is subject to the terms and conditions of the GNU General Public
* License.  See the file "COPYING" in the main directory of this archive
* for more details.
*
* 
* Copyright (c) 2021 Heinz-Juergen Oertel (hj.oertel@t-online.de)
*------------------------------------------------------------------
*
*/

/* use it for pr_info() and consorts */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include "defs.h"
#include "linux/delay.h"
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/iopoll.h>

int kvaser_pciefd_bus_on(struct kvaser_pciefd_can *can);
void kvaser_pciefd_pwm_start(struct kvaser_pciefd_can *can);
void kvaser_pciefd_pwm_stop(struct kvaser_pciefd_can *can);
void kvaser_pciefd_setup_controller(struct kvaser_pciefd_can *can);
void kvaser_pciefd_start_controller_flush(struct kvaser_pciefd_can *can);
void can_showmode(int minor);

/* int	CAN_Open = 0; */

/* timing values 
 in this case we need for all 10 possible timings one u32 each.
 This might be different on some controllers
 Beginning with the CAN FD support, multiples of the arbitration bit rate are needed
 *1, *2, *3, *4 *8 
 PlugFest Test is done using - Arbitration Phase Bit Rate  0.500 Mbit/s
- Data Phase Bit Rates
	* 1.000 Mbit/s
	* 2.000 Mbit/s
	* 4.000 Mbit/s
	* 5.000 Mbit/s
	* 6.667 Mbit/s
	* 8.000 Mbit/s
	* 10.00 Mbit/s
 */
#define ATIMINGS 10
#define DTIMINGS  5
u32 can_timing[ATIMINGS][DTIMINGS] = {
	/* arbitration rate	*1	*2	*4	*8 	     */
    	{ CAN_TIME_10K,	   0, 0, 0, 0},		
	{ CAN_TIME_20K,	   0, 0, 0, 0},
	{ CAN_TIME_50K,	   CAN_FTIME_50K,   CAN_FTIME_100K,  CAN_FTIME_200K,   CAN_FTIME_400K},
	{ CAN_TIME_100K,   CAN_FTIME_100K,  CAN_FTIME_200K,  CAN_FTIME_400K,   CAN_FTIME_800K},
	{ CAN_TIME_125K,   CAN_FTIME_125K,  CAN_FTIME_250K,  CAN_FTIME_500K,   CAN_FTIME_1000K},
	{ CAN_TIME_250K,   CAN_FTIME_250K,  CAN_FTIME_500K,  CAN_FTIME_1000K,  CAN_FTIME_2000K},
	{ CAN_TIME_500K,   CAN_FTIME_500K,  CAN_FTIME_1000K, CAN_FTIME_2000K,  CAN_FTIME_4000K},
	{ CAN_TIME_800K,   0, 0, 0, 0},
	{ CAN_TIME_1000K,  CAN_FTIME_1000K, CAN_FTIME_2000K, CAN_FTIME_4000K,  CAN_FTIME_8000K}
	};



/* Board reset
   means the following procedure:
  set Reset Request
  wait for Rest request is changing - used to see if board is available
  initialize board (with values from /proc/sys/Can)
    set output control register
    set timings
    set acceptance mask
*/


#ifdef DEBUG

void can_showIstat(int minor)
{
u32 istat;
    /* read interrupt status register */
    istat = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_ISTAT_REG);
    pr_info("Interrupt status istat = %08X\n", istat);
    if(istat & KVASER_PCIEFD_KCAN_IRQ_TAR)
	 pr_info("   istat Tx FIFO unaligned read\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_TAE)
	 pr_info("   istat Tx FIFO unaligned end\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_BPP)
	 pr_info("   istat Bus parameter protection error\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_IDLE)
	 pr_info("   istat Idle State\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_FDIC)
	 pr_info("   istat FDF bit when controller is in classic mode\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_ROF)
	 pr_info("   istat Rx FIFO overflow\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_ABD)
	 pr_info("   istat Abort done\n");
    if(istat &  KVASER_PCIEFD_KCAN_IRQ_TFD)
	 pr_info("   istat Tx buffer flush done\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_TOF)
	 pr_info("   istat Tx FIFO overflow\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_TE)
	 pr_info("   istat Tx FIFO empty\n");
    if(istat & KVASER_PCIEFD_KCAN_IRQ_TAL)
	 pr_info("   istat Transmitter unaligned\n");
}


/* when ever we need while debugging some controller status information */
void can_showstat(int minor)
{
u32 status;
u32 irq;

    if (proc_dbgmask && (proc_dbgmask & DBG_DATA)) {
	pr_info("can_showstat(minor %d)\n", minor);

	irq = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_IRQ_REG);
	status = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_STAT_REG);
	pr_info("   KCAN[%d] status reg = %08X, irq reg = %08X\n", minor, status, irq);

     if(status & KVASER_PCIEFD_KCAN_STAT_SRP) 
	 pr_info("   status request pending\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_AR) 
	 pr_info("   abort request\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_TXFR) 
	 pr_info("   transmitter flash request\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_IRQ) 
	 pr_info("   interrupt is signaled\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_IDLE) 
	 pr_info("   idle state. unit in reset mode, no abort or flash pending\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_BOFF) 
	 pr_info("   bus off, bus off recovery mode\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_TXE) 
	 pr_info("   tx is enabled\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_TXI) 
	 pr_info("   tx is idle, no transmit ongoing\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_RMR) 
	 pr_info("   reset mode requested\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_IRM) 
	 pr_info("   unit is in reset mode\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_CAP) 
	 pr_info("   hardware capability reg implemented\n");
     if(status & KVASER_PCIEFD_KCAN_STAT_FD) 
	 pr_info("   CAN FD is implemented\n");
     pr_info("   CMD SEQ No %d\n", (status >> 24) & 0xff);


     /* read number of packets in transmit buffer */
    status = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_TX_NPACKETS_REG);
    pr_info("   tx npackets max = %d, count = %d\n",
	    (status >> KVASER_PCIEFD_KCAN_TX_NPACKETS_MAX_SHIFT) & 0xff,
			(status & 0xFF));

    pr_info("   IRQ source:\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TAR)
	pr_info("   Tx FIFO unaligned read\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TAE)
	pr_info("   Tx FIFO unaligned end\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_BPP)
	pr_info("   Bus parameter protection error\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_FDIC)
	pr_info("   FDF bit when controller is in classic mode\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_ROF)
	pr_info("   Rx FIFO overflow\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_ABD)
	pr_info("   Abort done\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TFD)
	pr_info("   Transmit Buffer Flush Done\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TOF)
	pr_info("   Transmit FIFO overflow.\n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TE)
	pr_info("   Tx FIFO empty \n");
    if(irq & KVASER_PCIEFD_KCAN_IRQ_TAL)
	pr_info("   Transmitter unaligned\n");
    if(irq ==  0x0)
	pr_info("   No  KCAN Interrupt pending\n");
    }
    return;
}

void can_showpackagetype(int type)
{
    // printk("  received package type %d ", type);
    switch (type) {
	    case 8:
		    printk(".\tstatus packet\n");
		    break;
	    case 0:
		    printk(".\tdata packet\n");
		    break;
	    case 1:
		    printk(".\tack packet\n");
		    break;
	    case 2:
		    printk(".\tTXRQ packet\n");
		    break;
	    case 3:
		    printk(".\terror packet\n");
		    break;
	    case 4:
		    printk(".\tend of flush ack\n");
		    break;
	    case 5:
		    printk(".\terror frame\n");
		    break;
    }
}

void can_showmode(int minor)
{
u32 mode;
    mode = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_MODE_REG);
     pr_info("can_showmode(minor %d)\n", minor);
     pr_info("  CAN mode %08X, ewl = %d\n", mode, mode & 0xFF);
     if(mode & KVASER_PCIEFD_KCAN_MODE_RM) {
	 pr_info("  MODE_RM in reset mode\n");
     } else {
	 pr_info("  MODE_RM in active mode, not reset\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_LOM) {
	 pr_info("  MODE_LOM Listen only\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_DWH) {
	 pr_info("  MODE_DWH Dual header word\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_MRRQ)  {
	 pr_info("  MODE_MRRQ manual restart request\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_EPEN)  {
	 pr_info("  MODE_EPEN error package enabled\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_SM)  {
	 pr_info("  MODE_SM slave mode enable\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_ROP)  {
	 pr_info("  MODE_ROP restricted operation mode\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_NIFDEN)  {
	 pr_info("  MODE_NIFDEN non iso mode enabled\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_APT)  {
	 pr_info("  MODE_APT ack package: used data package ACK_DATA\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_AAM)  {
	 pr_info("  MODE_AAM auto ack mode: send dominat ack\n");
     } else {
	 pr_info("  MODE_AAM normal auto ack mode\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_EEN)  {
	 pr_info("  MODE_EEN active error flag enable\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_DPE)  {
	 pr_info("  MODE_DPE disable protocol exception\n");
     }
     if(mode & KVASER_PCIEFD_KCAN_MODE_CCM)  {
	 pr_info("  MODE_CCM classic CAN mode\n");
     }
     pr_info("  CHID CAN 0x%0x\n", (mode >> 16) & 0x7);
     pr_info("  EWL 0x%d\n", mode & 0xff);
}




#endif

/* can_getstat - read back as many status information as possible 
*
* Because the CAN protocol itself describes different kind of information
* already, and the driver has some generic information
* (e.g about it's buffers)
* we can define a more or less hardware independent format.
*
*
* exception:
* ERROR WARNING LIMIT REGISTER (EWLR)
* The SJA1000 defines a EWLR, reaching this Error Warning Level
* an Error Warning interrupt can be generated.
* The default value (after hardware reset) is 96. In reset
* mode this register appears to the CPU as a read/write
* memory. In operating mode it is read only.
* Note, that a content change of the EWLR is only possible,
* if the reset mode was entered previously. An error status
* change (see status register; Table 14) and an error
* warning interrupt forced by the new register content will not
* occur until the reset mode is cancelled again.
*/

int can_getstat(
	struct inode *inode,
	struct file *file,
	can_statuspar_t *stat
	)
{
unsigned int minor = iminor(inode);	
msg_fifo_t *Fifo;
unsigned long flags;
int rx_fifo = ((struct _instance_data *)(file->private_data))->rx_index;
u32 mode;

    stat->type = CAN_TYPE_KVASER_CANFD;

    stat->baud = proc_baud[minor];
    // stat->status = CAN_IN(minor, canstat);

    mode = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_MODE_REG);
    stat->error_warning_limit = mode & 0xFF;


    stat->error_code = 0; // CAN_IN(minor, errorcode); /* should reset this register */
    //
    // nr of errors? from the CAN FD IP core?
    stat->rx_errors = 0;
    stat->tx_errors = 0;
#if 1
    (void)inode;
    (void)file;
    (void)flags;

    (void)minor;
    (void)Fifo;
    (void)rx_fifo;
#else
#endif

    /* Disable CAN (All !!) Interrupts */
    /* !!!!!!!!!!!!!!!!!!!!! */
    /* save_flags(flags); cli(); */
    local_irq_save(flags);

    Fifo = &rx_buf[minor][rx_fifo];
    stat->rx_buffer_size = MAX_BUFSIZE;	/**< size of rx buffer  */
    /* number of messages */
    stat->rx_buffer_used =
    	(Fifo->head < Fifo->tail)
    	? (MAX_BUFSIZE - Fifo->tail + Fifo->head) : (Fifo->head - Fifo->tail);
    Fifo = &tx_buf[minor];
    stat->tx_buffer_size = MAX_BUFSIZE;	/* size of tx buffer  */
    /* number of messages */
    stat->tx_buffer_used = 
    	(Fifo->head < Fifo->tail)
    	? (MAX_BUFSIZE - Fifo->tail + Fifo->head) : (Fifo->head - Fifo->tail);
    /* Enable CAN Interrupts */
    /* !!!!!!!!!!!!!!!!!!!!! */
    /* restore_flags(flags); */
    local_irq_restore(flags);
    return 0;
}

/*
 * reset CAN controller
 * set CAN mode, if available
 * output control register 	proc_outc[minor]
 * can_set_timing(minor, proc_baud[minor]);
 * can_set_mask();
 */ 
int can_chip_reset(int minor)
{
int err;
    /* get the CAN base address base */ 
struct kvaser_pciefd *pcie;
struct kvaser_pciefd_can *can;

    pcie = pci_get_drvdata(can_pcidev[minor]);
    can = pcie->can[minor];

    DBGIN();



kvaser_pciefd_bus_on(can);




kvaser_pciefd_start_controller_flush(can);

    err = can_set_timing(minor, proc_baud[minor]);
    if(err)
	return -1;
    can_set_mask(minor, proc_acccode[minor], proc_accmask[minor]);

    /* can_dump(minor); */
    DBGOUT();
    return 0;
}


/*
 * Configures bit timing registers directly.
 * Chip must be in configuration mode.
 */
int can_set_btr(int minor, int btr0, int btr1)
{
    (void)minor;
    (void)btr0;
    (void)btr1;

    DBGIN();
    pr_err("direct bit timing register setting not supported yet\n");
    DBGOUT();
    return 0;
}


/*
 * Configures bit timing.
 * Chip must be in configuration mode.
 *
 * kvaser_pciefd_set_bittiming()
 */
int can_set_timing(int minor, int baud)
{
int index = 5;		/* index into bit timing table */
int findex = 1;		/* index into fast bit timing table */
int custom = 0;
int ret, isopen;
unsigned long irq_flags;    /* spin_lock_irqsave */ 
u32 mode, test, btrn;

typedef u32 array_of_DTIMINGS_u32_t[DTIMINGS];
array_of_DTIMINGS_u32_t *timing;

struct kvaser_pciefd *pcie;

    DBGIN();
    timing = can_timing;     /* timing is ISO timing in any case */

    pcie = pci_get_drvdata(can_pcidev[minor]);
/*****/
    isopen = atomic_read(&can_isopen[minor]);
    if ((isopen > 1) && (proc_baud[minor] != baud)) {
	DBGPRINT(DBG_DATA, ("isopen = %d", isopen));
	DBGPRINT(DBG_DATA, ("refused baud[%d]=%d already set to %d",
					minor, baud, proc_baud[minor]));
	return -1;
    }

    DBGPRINT(DBG_DATA, (KERN_CONT "base[%d]=0x%p\n", minor, (void *)proc_base[minor]));
    DBGPRINT(DBG_DATA, (KERN_CONT "pcie->can[%d]->reg_base 0x%p\n", minor,  pcie->can[minor]->reg_base));
    DBGPRINT(DBG_DATA, (KERN_CONT "baud[%d]=%d", minor, baud));
    mode = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_MODE_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "CAN mode 0x%08X = %d\n", mode, mode & 0xFF));
//   can_showmode(minor);

    switch(baud) {
	case   10: index = 0; break;
	case   20: index = 1; break;
	case   50: index = 2; break;
	case  100: index = 3; break;
	case  125: index = 4; break;
	case  250: index = 5; break;
	case  500: index = 6; break;
	case  800: index = 7; break;
	case 1000: index = 8; break;
	default  : 
		custom=1;
		break;
    }

	/* factor index
	 *   1	   1
	 *   2	   2
	 *   4	   3
	 *   8	   4 */
    findex = proc_speedfactor[minor];
    if ( findex == 4) findex = 3;
    if ( findex == 8) findex = 4;


    spin_lock_irqsave(&(pcie->can[minor]->lock), irq_flags);
    /* hardware depending code follows here */
    mode = ioread32(pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);

    /* Put the circuit in reset mode */
    iowrite32(mode | KVASER_PCIEFD_KCAN_MODE_RM,
	      pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);

    /* Can only set bittiming if in reset mode */
    ret = readl_poll_timeout(pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG,
			     test, test & KVASER_PCIEFD_KCAN_MODE_RM,
			     0, 10);

    if (ret) {
	    spin_unlock_irqrestore(&(pcie->can[minor]->lock), irq_flags);
	    return -EBUSY;
    }
    
    /* look at the IFI implementation */
    if (custom) {
	    /* FIXME: set direct register values */
	    pr_err("Custom bit timing setting not implemented yet\n");

    } else {
	/* use table values, index is index */

	btrn = timing[index][0];
	if(timing[index][0] == 0) {
	    pr_err("No valid arbitration timing value for index %d\n", index);
	    DBGOUT();
	    return -1;
	}
	iowrite32(btrn, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_BTRN_REG);

/* display CAN timing info */
	DBGPRINT(DBG_DATA, (" %d:%d   --- time = 0x%08x, ftime 0x%08x\n",
			index, findex,
			timing[index][0],
			timing[index][findex]));
	btrn = timing[index][findex];
	if(timing[index][findex] == 0) {
	    pr_err("No valid arbitration timing value for index %d, and data findex %d\n",
		    index, findex);
	    DBGOUT();
	    return -1;
	}
	iowrite32(btrn, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_BTRD_REG);
    }

    /* Restore previous reset mode status */
    iowrite32(mode, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);

    spin_unlock_irqrestore(&(pcie->can[minor]->lock), irq_flags);

    DBGOUT();
    return 0;
}


/*
   Reset error Counter information in /proc
   Clear pending Interrupts
   Set Interrupt sources
   Activate CAN

*/
int can_start_chip(int minor)
{
struct kvaser_pciefd *pcie;
struct kvaser_pciefd_can *can;
unsigned long irq;
u32 mode;
u32 istat;
u32 ien;
u32 hwcap;
// int err;

    DBGIN();
    /* get controller data */
    pcie = pci_get_drvdata(can_pcidev[minor]);
    can = pcie->can[minor];
    proc_rxerr[minor] = proc_txerr[minor] = 0L;

    istat = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_ISTAT_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "CAN istat %08X\n", istat));

    hwcap = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_HWCAP_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "CAN hwcap %08X\n", hwcap));

    // ien = 0x0001e02f;
    // iowrite32(ien, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);
    ien = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_IEN_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "KCAN ien %08X\n", ien));

    /* enable the CAN transceiver */
// kvaser_pciefd_pwm_start(pcie->can[minor]);

    /* for now, at least enable interrupts
     */
    spin_lock_irqsave(&can->lock, irq);
    /* clear all interrupts */
    iowrite32( 0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);
    iowrite32(0xffffffff, can->reg_base + KVASER_PCIEFD_KCAN_IRQ_REG);

    mode = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_MODE_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "CAN mode %08X, ewl = %d\n", mode, mode & 0xFF));
    /* CAN mode 00801560 = 96 
     * EEN	Active error flag enable
     * EPEN	Error packet enable 
     * DWH	Dual Header control bit, set == two word header
     * RM       Reset Mode
     *
     * */
/* set various KCAN mode register flags according to can->can.ctrlmode */
// kvaser_pciefd_setup_controller(can);
/************
 *   !!!!!!!!!!!!!!    wann muss CCM gesetzt werden ?
 *   reset CCM bit Classic CAN Mode
 *   kvaser_pciefd_setup_controller()
 * *******************/
    mode &=  ~KVASER_PCIEFD_KCAN_MODE_CCM; 

    if(noniso) {
	;
    }

    /* auf ISO / nonISO testen und einstellen
     * defs.h
	705:extern int noniso;

    	if(!noniso) {
..
} else }
}
*/
/* test */
    mode = ioread32(can->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);
    DBGPRINT(DBG_DATA, (KERN_CONT "CAN mode %08X, ewl = %d\n", mode, mode & 0xFF));
/* +++ */


    /* Reset interrupt handling */
    /* disable all int sources */
    iowrite32(0, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);
    /* reset pending irqs */
    iowrite32(0xffffffff, can->reg_base + KVASER_PCIEFD_KCAN_IRQ_REG);

    // kvaser_pciefd_set_tx_irq(can);
    ien =  
	  KVASER_PCIEFD_KCAN_IRQ_TE | 
	 /* KVASER_PCIEFD_KCAN_IRQ_ROF | */
	  KVASER_PCIEFD_KCAN_IRQ_TOF | KVASER_PCIEFD_KCAN_IRQ_ABD |
	  KVASER_PCIEFD_KCAN_IRQ_TAE | KVASER_PCIEFD_KCAN_IRQ_TAL |
	  KVASER_PCIEFD_KCAN_IRQ_FDIC | KVASER_PCIEFD_KCAN_IRQ_BPP |
	  KVASER_PCIEFD_KCAN_IRQ_TAR | KVASER_PCIEFD_KCAN_IRQ_TFD;

// pr_info(" set KCAN IEN to 0x%08X\n", ien);
    iowrite32(ien, can->reg_base + KVASER_PCIEFD_KCAN_IEN_REG);



    /* it makes more sense to leave reset mode here */
    /* remove reset bit */
    mode &= ~KVASER_PCIEFD_KCAN_MODE_RM;
    iowrite32(mode, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);
    /* wait for Reset mode to be completed */

    /* lock erst zum Schluss frei geben ? */
    // spin_unlock_irqrestore(&(pcie->can[minor]->lock), irq);
    spin_unlock_irqrestore(&can->lock, irq);

    if (!wait_for_completion_timeout(&can->start_comp,
				     KVASER_PCIEFD_WAIT_TIMEOUT)) {
	    netdev_err(can->can.dev, "Timeout during bus on reset\n");
	    return -ETIMEDOUT;
    }


    can->can.state = CAN_STATE_ERROR_ACTIVE;
    // netif_wake_queue(can->can.dev);
    can->bec.txerr = 0;
    can->bec.rxerr = 0;
    can->err_rep_cnt = 0;

#define KVASER_PCIEFD_IRQ_REG 0x40
#define KVASER_PCIEFD_IEN_REG 0x50
// pr_info(" pci IRQ 0x%08X\n",
// 	ioread32(pcie->reg_base + KVASER_PCIEFD_IRQ_REG));
// pr_info(" pci IEN 0x%08X\n",
// 	ioread32(pcie->reg_base + KVASER_PCIEFD_IEN_REG));

// can_showIstat(minor);
// can_showmode(minor);
// can_showstat(minor);

    DBGOUT();
    return 0;
}


/*
* If the driver is used by more than one application,
* one should take care that this functionality (like some others)
* can not be called by any application.
* Stopping the shared CAN will stop it for all other processes as well.
*
* can4linux blocks this function (and others)  in ioctl.c
*/
int can_stopchip(int minor)
{
struct kvaser_pciefd *pcie;

    DBGIN();
    /* get controller data */
    pcie = pci_get_drvdata(can_pcidev[minor]);
    /* stop CAN FD transceiver * Transmitter power control  */ 
    // kvaser_pciefd_pwm_stop(pcie->can[minor]);

    DBGOUT();
    return 0;
}

/* set value of the output control register */
int can_set_mode(int minor, int arg)
{
    (void)minor;
    (void)arg;

    DBGIN();

    DBGOUT();
    return 0;
}

/*
Listen-Only Mode
In listen-only mode, the CAN module is able to receive messages
without giving an acknowledgment.
Since the module does not influence the CAN bus in this mode
the host device is capable of functioning like a monitor
or for automatic bit-rate detection.

 must be done after CMD_STOP (can_stopchip)
 and before CMD_START (can_start_chip)
*/
int can_set_listenonlymode(int minor,
	int arg)	/* true - set Listen Only, false - reset */
{
    (void)minor;
    (void)arg;

    DBGIN();
    if (arg) {
	/* set listen only mode */
	;
    } else {
	/* set active mode */
	;
    }

    DBGOUT();
    return 0;
}

/* set Acceptance Code and Mask Registers */
int can_set_mask (int minor, unsigned int code, unsigned int mask)
{
    (void)minor;
    (void)code;
    (void)mask;

    DBGIN();
    /* set register values */

    /* put values back in global variables for sysctl */
    proc_acccode[minor] = code;
    proc_accmask[minor] = mask;
    DBGOUT();
    return 0;
}


/* 
Single CAN frames or the very first Message are copied into the CAN controller
using this function. After that an transmission request is set in the
CAN controllers command register.
After a successful transmission, an interrupt will be generated,
which will be handled in the CAN ISR CAN_Interrupt()


Kvaser KCAN     (CAN FD)
Every CAN controller has one FIFO buffer for messages to be transmitted

Every packet in the transmit FIFO will create an acknowledge packet
that is written back to the receive FIFO when the operation has been finished.
The acknowledge packet contains the sequence number of the data or control packet.

Note: Header word 2 and 3 has no functionality for transmitted packets.
Important: Header word 2 and 3 must not be transmitted for packets with DLC=0, it is important
that header word 1 is tagged as last word in the FIFO buffer.
The DWH=0 mode is offered as a convenience, making it possible to use the same format for
both received and transmitted packets.
The FIFO access order when writing to the transmit buffer FIFO with
KCAN.KCAN_MODE.DWH=1 shall be:


A 64 byte CAN FD packet write transfer can be done using FIFO mode as follows
- Write two 32-bit header words and 15 32-bit data words to addresses 79-95
  (FIFO word range)
- Write one 32-bit word to address 96 (FIFO Last Word Range)

The last write will store the completed packet in the transmit FIFO queue.
Not until the last write will the FIFO packet count be incremented.




*/
int can_send_message(int minor, canmsg_t *tx)
{
int i = 0;
u32 fifostatus = 0;
u32 dlc;
u8 count;
int nwords;	/* number of 4 byte words to be written to tx data FIFO */
struct kvaser_pciefd_tx_packet packet;
struct kvaser_pciefd *pcie;
struct kvaser_pciefd_can *can;
static int seq = 0;

    DBGIN();
    /* get pci struct */
    pcie = pci_get_drvdata(can_pcidev[minor]);
    /* get controller data */
    can = pcie->can[minor];
    // seq = can->echo_idx;

#if 1 /* nur test , macht can_start_chip()*/
    dlc = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_MODE_REG);
    /* remove reset bit */
    dlc &= ~KVASER_PCIEFD_KCAN_MODE_RM;
    iowrite32(dlc, pcie->can[minor]->reg_base + KVASER_PCIEFD_KCAN_MODE_REG);
#endif

can_showstat(minor);

    DBGPRINT(DBG_DATA,
	    (KERN_CONT "CAN[%d]: tx.flags=%d tx.id=0x%lx tx.length=%d stat=0x%x",
	    minor, tx->flags, (long unsigned int)tx->id,
	    tx->length, fifostatus));

    DBGPRINT(DBG_DATA, (KERN_CONT " Switching Bit Timing factor %d\n",
				    proc_speedfactor[minor]));

    /* wait for transmission complete, read canstat 
    while ( ! ((stat=CANin(minor, canstat)) & CAN_TRANSMIT_BUFFER_ACCESS)) {

	    #if LINUX_VERSION_CODE >= 131587
	    
	    cond_resched();
	    # else
	    if( current->need_resched ) schedule();
	    # endif
	    #else
	    if( need_resched ) schedule();
	    #endif
    }
    */


    /* To send a message, the TX FIFO must be filled
     * kvaser uses for this the tx package struct
     * fill in message id, message data, .... 
struct kvaser_pciefd_tx_packet {
	u32 header[2];
	u8 data[64];
};

kvaser_pciefd_start_xmit();
*/
/*
kvaser_pciefd_prepare_tx_packet(struct kvaser_pciefd_tx_packet *p,.. */

	memset(&packet, 0, sizeof(packet));

	/* message id */
	packet.header[0] = tx->id;
	/* handle Single shot mode  SSM */
	   /*    */ 
	/* RTR */
	if (tx->flags & MSG_RTR)
	    packet.header[0] |= KVASER_PCIEFD_RPACKET_RTR;
	/* extended id frame */
	if (tx->flags & MSG_EXT)
	    packet.header[0] |= KVASER_PCIEFD_RPACKET_IDE;

	/* handle FDF flag for longer frames */
	if (tx->flags & MSG_CANFD) {
	    packet.header[1] |= KVASER_PCIEFD_RPACKET_FDF;
	    if (
		(proc_speedfactor[minor] > 1)
		&&
		(tx->flags & MSG_RBRS)) {
		/* set Bit Rate Switch bit if 
		 * BRS bit is set && Speedfactor is set in /proc */
		packet.header[1] |= KVASER_PCIEFD_RPACKET_BRS;
	    }
	}

	DBGPRINT(DBG_DATA,(KERN_CONT "in dlc %d, out dlc %d\n",
		    tx->length, len2dlc(tx->length))); 

	/* DLC */
	dlc = len2dlc(tx->length); 
	packet.header[1] |= (dlc & 0x0f) << KVASER_PCIEFD_RPACKET_DLC_SHIFT;

	packet.header[1] |= seq & KVASER_PCIEFD_PACKET_SEQ_MSK;
	seq++;



/* handle CAN FD Flags  BRS and ESI */
	if (proc_speedfactor[minor] > 1) {
	    ;
	}

/* /handle CAN FD Flags  BRS and ESI */

	nwords = DIV_ROUND_UP(tx->length, 4);
// pr_info("transmit package has %d nwords\n", nwords);

	/* AREQ
	 * Acknowledge Request. If set an ACK or ACK_DATA
	 * (depending on ACK type setting KCAN_MODE.APT, section 6.9)
	 * packet is returned in the receiver buffer
	 * after a successful transmit of the packet.
	 */
	// packet.header[1] |= KVASER_PCIEFD_TPACKET_AREQ;

	/* sequence counter, 8bit, , used in the acknowledge receive */
	/* not used yet */
	// packet.header[1] |= 0; /* |= seq & KVASER_PCIEFD_PACKET_SEQ_MSK; */


	/* copy data bytes in the packet struct */
	memcpy(packet.data, tx->data, tx->length);


    /* Write header to fifo */
    iowrite32(packet.header[0],
	      (void *)proc_base[minor] + KVASER_PCIEFD_KCAN_FIFO_REG);
    iowrite32(packet.header[1],
	      (void *)proc_base[minor] + KVASER_PCIEFD_KCAN_FIFO_REG);

    if (nwords) {
	u32 data_last = ((u32 *)packet.data)[nwords - 1];


/* wie ist der mode hier ? */
// can_showmode(minor);
// can_showstat(minor);
/* last */

/* ==== geht noch ================ */
// DBGOUT();
// return 0;

	/* Write data to fifo, except last word */
	iowrite32_rep((void *)proc_base[minor] +
		      KVASER_PCIEFD_KCAN_FIFO_REG, packet.data,
		      nwords - 1);
	/* Write last word to end of fifo */
	__raw_writel(data_last, (void *)proc_base[minor] +
		     KVASER_PCIEFD_KCAN_FIFO_LAST_REG);
    } else {
// printk(" nwords == 0\n");
	/* Complete write to fifo */
	__raw_writel(0, (void *)proc_base[minor] +
		     KVASER_PCIEFD_KCAN_FIFO_LAST_REG);
    }

    count = ioread32((void *)proc_base[minor] + KVASER_PCIEFD_KCAN_TX_NPACKETS_REG);



//udelay(10000);

    // spin_unlock_irqrestore(&can->echo_lock, irq_flags);

    /* issue transmission request to the CAN controller */

#if 1 /* war 1 */
    /* 
     * Save last message that was sent.
     * Since can4linux 3.5 multiple processes can access
     * one CAN interface. On a CAN interrupt this message is copied into 
     * the receive queue of each process that opened this same CAN interface.
     */
    memcpy(
	(void *)&last_tx_object[minor],
	(void *)tx,
	sizeof(canmsg_t));
#endif /* last message */

    DBGOUT();
    return i;
} /* can_send_message */



/*
 * The plain interrupt
 * based on the kvaser_pciefd.c code
 *
 * With this board the irq handler is in 
 * kvaser_pciefd.c (so far)
 */



#if 0
irqreturn_t can_interrupt(int irq, void *dev_id)
{
int minor;
int rx_fifo;
struct timeval  timestamp;
unsigned long flags;
int ext;			/* flag for extended message format */
int irqsrc, dummy;
msg_fifo_t   *RxFifo; 
msg_fifo_t   *TxFifo;
#if CAN_USE_FILTER
msg_filter_t *RxPass;
unsigned int id;
#endif 
#if 1
int first;
#endif 
unsigned int ecc;		/* error counter */


	(void)ecc;
	(void)dummy;
	(void)ext;
	(void)flags;

#if CONFIG_TIME_MEASURE
    set_measure_pin();
#endif

    first  = 0;
    irqsrc = 0;


    minor = *(int *)dev_id;
    /* pr_info("CAN - ISR ; minor = %d\n", *(int *)dev_id); */

    RxFifo = &rx_buf[minor][0]; 
    TxFifo = &tx_buf[minor];
#if CAN_USE_FILTER
    RxPass = &Rx_Filter[minor];
#endif 



#if 0
    /* read status if CAN has an interrupt pending */
    /*
    irqsrc = CAN_IN(minor, canirq);
    */


    if(irqsrc == 0) {
         /* first call to ISR, it's not for me ! */
#if CONFIG_TIME_MEASURE
	reset_measure_pin();
#endif
	return IRQ_RETVAL(IRQ_NONE);
    }

    /* Whatever interrupt we have, update the tx error counter
     * and rx error counter information in /proc/sys/dev/Can
     */

    /*
    proc_txerrcounter[minor] = CAN_IN(minor, txerror);
    proc_rxerrcounter[minor] = CAN_IN(minor, rxerror);
    */

    do {
    /* loop as long as the CAN controller shows interrupts */
    /* can_dump(minor); */
#if defined(DEBUG)
    /* how often do we loop through the ISR ? */
    /* if(first) pr_info("n = %d\n", first); */
    /* we can have a /proc/sys/dev/Can/irqloop
       to store the max counter value
       for debugging purposes to see how heavy the isr is used
       */
	first++;
	if (first > 10) return IRQ_RETVAL(IRQ_HANDLED);
#endif

	get_timestamp(minor, &timestamp);

	for(rx_fifo = 0; rx_fifo < CAN_MAX_OPEN; rx_fifo++) {
	    RxFifo = &rx_buf[minor][rx_fifo];

	    RxFifo->data[RxFifo->head].timestamp = timestamp;

	    /* preset flags */
	    (RxFifo->data[RxFifo->head]).flags =
			    (RxFifo->status & BUF_OVERRUN ? MSG_BOVR : 0);
	}


	/*========== receive interrupt */
	if( irqsrc & CAN_RECEIVE_INT ) {

	}

	/*========== transmit interrupt */
	if( irqsrc & CAN_TRANSMIT_INT ) {

	    /* check for tx fifo emty */
	    goto tx_done;

	}
tx_done:

	/*========== arbitration lost */
	if( irqsrc &  CAN_ARBITR_LOST_INT) {
	    proc_arbitrationlost[minor]++; 
	}

	/*========== error status */
	if( irqsrc & (
	      CAN_ERROR_WARN_INT 
	    | CAN_ERROR_PASSIVE_INT
	    | CAN_BUS_ERR_INT
		)) {

	}
	/*========== CAN data overrun interrupt */
	if( irqsrc & CAN_OVERRUN_INT ) {

	}


    } while( irqsrc != 0 );

#endif /* 0 */
    DBGPRINT(DBG_DATA, (" => leave IRQ[%d]", minor));

    /*
     * this function is board, not CAN controller specific */
    board_clear_interrupts(minor);

#if CONFIG_TIME_MEASURE
    reset_measure_pin();
#endif

    return IRQ_RETVAL(IRQ_HANDLED);
}
#endif
